home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 201-225 / disk_218 / mandel / src / minrexx.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  14KB  |  430 lines

  1. /*
  2.  *   This is an example of how REXX messages might be handled.    This is
  3.  *   a `minimum' example that both accepts asynchronous REXX messages and
  4.  *   can request REXX service.
  5.  *
  6.  *   Read this entire file!  It's short enough.
  7.  *
  8.  *   It is written in such a fashion that it can be attached to a program
  9.  *   with a minimum of fuss.  The only external symbols it makes available
  10.  *   are the seven functions and RexxSysBase.
  11.  *
  12.  *   This code is by Radical Eye Software, but it is put in the public
  13.  *   domain.  I would appreciate it if the following string was left in
  14.  *   both as a version check and as thanks from you for the use of this
  15.  *   code.
  16.  *
  17.  *   If you modify this file for your own use, don't bump the version
  18.  *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
  19.  *   don't have fake `versions' floating around.
  20.  *
  21.  *   I [Olaf Seibert] changed it a bit for Mandel, that has its own
  22.  *   kind of association list and command dispatcher. And I adjusted some
  23.  *   comments to match the code.
  24.  */
  25. static char *blurb = "Radical Eye MinRexx 0.4 for Mandel" ;
  26. /*
  27.  *   We read in our own personal little include.
  28.  */
  29. #include "minrexx.h"
  30. /*
  31.  *   All of our local globals, hidden from sight.
  32.  */
  33. static struct MsgPort *rexxPort ;       /* this is *our* rexx port */
  34. static int bringerdown ;           /* are we trying to shut down? */
  35. static struct rexxCommandList *globalrcl ; /* our command association list */
  36. static long stillNeedReplies ;           /* how many replies are pending? */
  37. static long rexxPortBit ;           /* what bit to wait on for Rexx? */
  38. static char *extension ;           /* the extension for macros */
  39. static int (*userdisp)() ;                 /* the user's dispatch function */
  40. struct RexxMsg *oRexxMsg ;           /* the outstanding Rexx message */
  41. /*
  42.  *   Our library base.    Don't you dare close this!
  43.  */
  44. struct RxsLib *RexxSysBase ;
  45. /*
  46.  *   This is the main entry point into this code.
  47.  */
  48. long upRexxPort(s, rcl, exten, uf)
  49. /*
  50.  *   The first argument is the name of your port to be registered;
  51.  *   this will be used, for instance, with the `address' command of ARexx.
  52.  */
  53. char *s ;
  54. /*
  55.  *   The second argument is an association list of command-name/user-data
  56.  *   pairs.  It's an array of struct rexxCommandList, terminated by a
  57.  *   structure with a NULL in the name field. The commands are case
  58.  *   sensitive.  The user-data field can contain anything appropriate,
  59.  *   perhaps a function to call or some other data.
  60.  */
  61. struct rexxCommandList *rcl ;
  62. /*
  63.  *   The third argument is the file extension for ARexx macros invoked
  64.  *   by this program.  If you supply this argument, any `primitive' not
  65.  *   in the association list rcl will be sent out to ARexx for
  66.  *   interpretation, thus allowing macro programs to work just like
  67.  *   primitives.  If you do not want this behavior, supply a `NULL'
  68.  *   here, and those commands not understood will be replied with an
  69.  *   error value of RXERRORNOCMD.
  70.  */
  71. char *exten ;
  72. /*
  73.  *   The fourth argument is the user dispatch function.  This function
  74.  *   will *only* be called from dispRexxPort(), either from the user calling
  75.  *   this function directly, or from dnRexxPort().  Anytime a command
  76.  *   match is found in the association list, this user-supplied function
  77.  *   will be called with three arguments---the Rexx message that was
  78.  *   received, a pointer to the association pair, and a pointer to the
  79.  *   command string.
  80.  *   Note that the user function should never ReplyMsg() the message;
  81.  *   instead he should indicate the return values with replyRexxCmd();
  82.  *   otherwise we lose track of the messages that still lack replies.
  83.  */
  84. int (*uf)() ;
  85. /*
  86.  *   upRexxPort() returns the signal bit to wait on for Rexx messages.
  87.  *   If something goes wrong, it simply returns a `0'.  Note that this
  88.  *   function is safe to call multiple times because we check to make
  89.  *   sure we haven't opened already.  It's also a quick way to change
  90.  *   the association list or dispatch function.
  91.  */
  92. {
  93.    struct MsgPort *FindPort() ;
  94.    struct MsgPort *CreatePort() ;
  95.  
  96. /*
  97.  *   Some basic error checking.
  98.  */
  99.  /*
  100.    if (rcl == NULL || uf == NULL)
  101.       return(0L) ;
  102.   */
  103. /*
  104.  *   If we aren't open, we make sure no one else has opened a port with
  105.  *   this name already.  If that works, and the createport succeeds, we
  106.  *   fill rexxPortBit with the value to return.
  107.  *
  108.  *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
  109.  *   for rexxPort == NULL also insures that our rexxPortBit is 0.
  110.  */
  111.    if (rexxPort == NULL) {
  112.       Forbid() ;
  113.       if (FindPort(s)==NULL)
  114.      rexxPort = CreatePort(s, 0L) ;
  115.       Permit() ;
  116.       if (rexxPort != NULL)
  117.      rexxPortBit = 1L << rexxPort->mp_SigBit ;
  118.    }
  119. /*
  120.  *   Squirrel away these values for our own internal access, and return
  121.  *   the wait bit.
  122.  */
  123.    globalrcl = rcl ;
  124.    extension = exten ;
  125.    userdisp = uf ;
  126.    return(rexxPortBit) ;
  127. }
  128. /*
  129.  *   This function closes the rexx library, but only if it is open
  130.  *   and we aren't expecting further replies from REXX.  It's
  131.  *   *private*, but it doesn't have to be; it's pretty safe to
  132.  *   call anytime.
  133.  */
  134. static void closeRexxLib() {
  135.    if (stillNeedReplies == 0 && RexxSysBase) {
  136.       CloseLibrary(RexxSysBase) ;
  137.       RexxSysBase = NULL ;
  138.    }
  139. }
  140. /*
  141.  *   This function closes down the Rexx port.  It is always safe to
  142.  *   call, and should *definitely* be made a part of your cleanup
  143.  *   routine.  No arguments and no return.  It removes the Rexx port,
  144.  *   replies to all of the messages and insures that we get replies
  145.  *   to all the ones we sent out, closes the Rexx library, deletes the
  146.  *   port, clears a few flags, and leaves.
  147.  */
  148. void dnRexxPort() {
  149.    if (rexxPort) {
  150.       RemPort(rexxPort) ;
  151.       bringerdown = 1 ;
  152. /*
  153.  *   A message still hanging around?  We kill it off.
  154.  */
  155.       if (oRexxMsg) {
  156.      oRexxMsg->rm_Result1 = RXERRORIMGONE ;
  157.      ReplyMsg(oRexxMsg) ;
  158.      oRexxMsg = NULL ;
  159.       }
  160.       while (stillNeedReplies) {
  161.      WaitPort(rexxPort) ;
  162.      dispRexxPort() ;
  163.       }
  164.       closeRexxLib() ;
  165.       DeletePort(rexxPort) ;
  166.       rexxPort = NULL ;
  167.    }
  168.    rexxPortBit = 0 ;
  169. }
  170. /*
  171.  *   Here we dispatch any REXX messages that might be outstanding.
  172.  *   This is the main routine for handling Rexx messages.
  173.  *   This function is fast if no messages are outstanding, so it's
  174.  *   pretty safe to call fairly often.
  175.  *
  176.  *   If we are bring the system down and flushing messages, we reply
  177.  *   with a pretty serious return code RXERRORIMGONE.
  178.  *
  179.  *   No arguments, no returns.
  180.  */
  181. void dispRexxPort() {
  182.    register struct RexxMsg *GetMsg() ;
  183.    register struct RexxMsg *RexxMsg ;
  184.    register char *p ;
  185.    register int dontreply ;
  186.  
  187. /*
  188.  *   If there's no rexx port, we're out of here.
  189.  */
  190.    if (rexxPort == NULL)
  191.       return ;
  192. /*
  193.  *   Otherwise we have our normal loop on messages.
  194.  */
  195.    while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort)) {
  196. /*
  197.  *   If we have a reply to a message we sent, we look at the second
  198.  *   argument.    If it's set, it's a function we are supposed to call
  199.  *   so we call it.  Then, we kill the argstring and the message
  200.  *   itself, decrement the outstanding count, and attempt to close
  201.  *   down the Rexx library.  Note that this call only succeeds if
  202.  *   there are no outstanding messages.  Also, it's pretty quick, so
  203.  *   don't talk to me about efficiency.
  204.  */
  205.       if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
  206.      if (RexxMsg->rm_Args[1]) {
  207.         ((int (*)())(RexxMsg->rm_Args[1]))(RexxMsg) ;
  208.      }
  209.      DeleteArgstring(RexxMsg->rm_Args[0]) ;
  210.      DeleteRexxMsg(RexxMsg) ;
  211.      stillNeedReplies-- ;
  212.      closeRexxLib() ;
  213. /*
  214.  *   The default case is we got a message and we need to check it for
  215.  *   primitives.  We skip past any initial tabs or spaces and initialize
  216.  *   the return code fields.
  217.  */
  218.       } else {
  219.      p = (char *)RexxMsg->rm_Args[0] ;
  220.      while (*p > 0 && *p <= ' ')
  221.         p++ ;
  222.      RexxMsg->rm_Result1 = 0 ;
  223.      RexxMsg->rm_Result2 = 0 ;
  224. /*
  225.  *   If somehow the reply is already done or postponed, `dontreply' is
  226.  *   set.
  227.  */
  228.      dontreply = 0 ;
  229. /*
  230.  *   If the sky is falling, we just blow up and replymsg.
  231.  */
  232.      if (bringerdown) {
  233.         RexxMsg->rm_Result1 = RXERRORIMGONE ;
  234. /*
  235.  *   Otherwise we call ExecuteBatchCommand() to find and execute
  236.  *   the command.
  237.  */
  238.      } else {
  239.         int result;
  240.  
  241.         oRexxMsg = RexxMsg ;
  242.         result = ExecuteBatchCommand(p);
  243. /*
  244.  *   If we did get -1, we didn't understand the command.  In
  245.  *   this case, if we were supplied an extension in upRexxPort, we know
  246.  *   that we should send the command out, so we do so, synchronously.
  247.  *   The synchronous send takes care of our reply.  If we were given a
  248.  *   NULL extension, we bitch that the command didn't make sense to us.
  249.  */
  250.         if (result == -1) {
  251.            if (extension) {
  252.           syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg) ;
  253.           dontreply = 1 ;
  254.            } else {
  255.           RexxMsg->rm_Result1 = RXERRORNOCMD ;
  256.            }
  257.         } else if (result == 0) {   /* Command failed */
  258.            RexxMsg->rm_Result1 = 20;
  259.            RexxMsg->rm_Result2 = 10;
  260.         }
  261.      }
  262. /*
  263.  *   Finally, reply if appropriate.
  264.  */
  265.      oRexxMsg = NULL ;
  266.      if (! dontreply)
  267.         ReplyMsg(RexxMsg) ;
  268.       }
  269.    }
  270. }
  271. /*
  272.  *   Opens the Rexx library if unopened.  Returns success (1) or
  273.  *   failure (0).  This is another function that is *private* but
  274.  *   that doesn't have to be.
  275.  */
  276. static int openRexxLib() {
  277.    struct RxsLib *OpenLibrary() ;
  278.  
  279.    if (RexxSysBase)
  280.       return(1) ;
  281.    return((RexxSysBase = OpenLibrary(RXSNAME, 0L)) != NULL) ;
  282. }
  283. /*
  284.  *   This is the general ARexx command interface, but is not the one
  285.  *   you will use most of the time; ones defined later are easier to
  286.  *   understand and use.  But they all go through here.
  287.  */
  288. struct RexxMsg *sendRexxCmd(s, f, p1, p2, p3)
  289. char *s ;
  290. /*
  291.  *   The first parameter is the command to send to Rexx.
  292.  */
  293. int (*f)() ;
  294. /*
  295.  *   The second parameter is either NULL, indicating that the command
  296.  *   should execute asynchronously, or a function to be called when the
  297.  *   message we build up and send out here finally returns.  Please note
  298.  *   that the function supplied here could be called during cleanup after
  299.  *   a fatal error, so make sure it is `safe'.  This function always is
  300.  *   passed one argument, the RexxMsg that is being replied.
  301.  */
  302. STRPTR p1, p2, p3 ;
  303. /*
  304.  *   These are up to three arguments to be stuffed into the RexxMsg we
  305.  *   are building up, making the values available when the message is
  306.  *   finally replied to.  The values are stuffed into Args[2]..Args[4].
  307.  */
  308. {
  309.    struct RexxMsg *CreateRexxMsg() ;
  310.    STRPTR CreateArgstring() ;
  311.    register struct MsgPort *rexxport ;
  312.    register struct RexxMsg *RexxMsg ;
  313.  
  314. /*
  315.  *   If we have too many replies out there, we just return failure.
  316.  *   Note that you should check the return code to make sure your
  317.  *   message got out!  Then, we forbid, and make sure that:
  318.  *    - we have a rexx port open
  319.  *    - Rexx is out there
  320.  *    - the library is open
  321.  *    - we can create a message
  322.  *    - we can create an argstring
  323.  *
  324.  *   If all of these succeed, we stuff a few values and send the
  325.  *   message, permit, and return.
  326.  */
  327.    if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1)
  328.       return(NULL) ;
  329.    RexxMsg = NULL ;
  330.    if (openRexxLib() && (RexxMsg =
  331.          CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
  332.          (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s)))) {
  333.       RexxMsg->rm_Action = RXCOMM ;
  334.       RexxMsg->rm_Args[1] = (STRPTR)f ;
  335.       RexxMsg->rm_Args[2] = p1 ;
  336.       RexxMsg->rm_Args[3] = p2 ;
  337.       RexxMsg->rm_Args[4] = p3 ;
  338.       RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ;
  339.       Forbid() ;
  340.       if (rexxport = FindPort(RXSDIR))
  341.      PutMsg(rexxport, RexxMsg) ;
  342.       Permit() ;
  343.       if (rexxport) {
  344.      stillNeedReplies++ ;
  345.      return(RexxMsg) ;
  346.       } else
  347.      DeleteArgstring(RexxMsg->rm_Args[0]) ;
  348.    }
  349.    if (RexxMsg)
  350.       DeleteRexxMsg(RexxMsg) ;
  351.    closeRexxLib() ;
  352.    return(NULL) ;
  353. }
  354. /*
  355.  *   This function is used to send out an ARexx message and return
  356.  *   immediately.  Its single parameter is the command to send.
  357.  */
  358. struct RexxMsg *asyncRexxCmd(s)
  359. char *s ;
  360. {
  361.    return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ;
  362. }
  363. /*
  364.  *   This function sets things up to reply to the message that caused
  365.  *   it when we get a reply to the message we are sending out here.
  366.  *   But first the function we pass in, which actually handles the reply.
  367.  *   Note how we get the message from the Args[2]; Args[0] is the command,
  368.  *   Args[1] is this function, and Args[2]..Args[4] are any parameters
  369.  *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
  370.  *   along.
  371.  */
  372. static void replytoit(msg)
  373. register struct RexxMsg *msg ;
  374. {
  375.    register struct RexxMsg *omsg ;
  376.  
  377.    omsg = (struct RexxMsg *)(msg->rm_Args[2]) ;
  378.    replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ;
  379.    ReplyMsg(omsg) ;
  380. }
  381. /*
  382.  *   This function makes use of everything we've put together so far,
  383.  *   and functions as a synchronous Rexx call; as soon as the macro
  384.  *   invoked here returns, we reply to `msg', passing the return codes
  385.  *   back.
  386.  */
  387. struct RexxMsg *syncRexxCmd(s, msg)
  388. char *s ;
  389. struct RexxMsg *msg ;
  390. {
  391.    return(sendRexxCmd(s, (APTR)&replytoit, msg, NULL, NULL)) ;
  392. }
  393. /*
  394.  *   There are times when you want to pass back return codes or a
  395.  *   return string; call this function when you want to do that.
  396.  */
  397. void replyRexxCmd(msg, primary, secondary, string)
  398. /*
  399.  *   The first parameter is the message we are replying to.
  400.  */
  401. register struct RexxMsg *msg ;
  402. /*
  403.  *   The next two parameters are the primary and secondary return
  404.  *   codes.
  405.  */
  406. register long primary, secondary ;
  407. /*
  408.  *   The final parameter is a return string.  This string is only
  409.  *   returned if the primary return code is 0, and a string was
  410.  *   requested.
  411.  */
  412. register char *string ;
  413. {
  414.    STRPTR CreateArgstring() ;
  415.  
  416. /*
  417.  *   Note how we make sure the Rexx Library is open before calling
  418.  *   CreateArgstring . . . and we close it down at the end, if possible.
  419.  */
  420.    if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) {
  421.       if (string && openRexxLib())
  422.      secondary = (long)CreateArgstring(string, (long)strlen(string)) ;
  423.       else
  424.      secondary = 0L ;
  425.    }
  426.    msg->rm_Result1 = primary ;
  427.    msg->rm_Result2 = secondary ;
  428.    closeRexxLib() ;
  429. }
  430.